package name.cyrax.red.jpv.impl.funcs.core;

import name.cyrax.red.jpv.api.adapters.DoubleAdapter;
import name.cyrax.red.jpv.api.adapters.IntegerAdapter;
import name.cyrax.red.jpv.api.adapters.LongAdapter;
import name.cyrax.red.jpv.api.adapters.NumericAccessor;
import name.cyrax.red.jpv.api.adapters.NumericAdapter;
import name.cyrax.red.jpv.api.funcs.FunctorImplementor;
import name.cyrax.red.jpv.api.funcs.Invocable;
import name.cyrax.red.jpv.api.funcs.InvocationContext;
import name.cyrax.red.jpv.impl.adapters.NumericImpl;
import name.cyrax.red.jpv.impl.funcs.InvocableImpl;

public abstract class FunctorAbstractBinaryImpl<A extends NumericAdapter> 
	extends FunctorImpl<A, FunctorImplementor<A>> 
	implements FunctorImplementor<FunctorImplementor<A>>
{
	protected class BinaryInvocableImpl<V extends Number> extends InvocableImpl<V> {
		protected final Invocable<?> param0;
		protected final Invocable<?> param1;

		public BinaryInvocableImpl(Invocable<V> param0, Invocable<?> param1)
		{
			super(param0.factory());
			this.param0 = param0;
			this.param1 = param1;
		}
		
		@Override public <T> T access(InvocationContext context, NumericAccessor<T> accessor)
		{
			final FunctorImplementor<A> l0 = newImplementation(context, param0);
			NumericAdapter l1 = param1.access(context, new NumericAccessor<A>() {

				@Override public A asInteger(IntegerAdapter adapter) {
					return l0.newAdapter(adapter); 
				}

				@Override public A asLong(LongAdapter adapter) {
					return l0.newAdapter(adapter);
				}

				@Override public A asDouble(DoubleAdapter adapter) {
					return l0.newAdapter(adapter);
				}
			});
			return l1.detectType(accessor);
		}

		@Override public Invocable<?> getInvocableParam(int index)
		{
			return NumericImpl.switchOf(index, param0, param1);
		}

		@Override protected String getName()
		{
			return FunctorAbstractBinaryImpl.this.getName();
		}
	};

	@Override protected FunctorImplementor<FunctorImplementor<A>> getPrimaryFunctorImplementor() {
		return this;
	}
}
